import { MathUtil } from "./MathUtil";

const MAX_COLOR = 256 * 256 * 256;

/**
 * 随机数
 */
export class Rand {
	constructor(
		/** 生成 [0,1) 区间范围的随机数 */
		public rand: () => number = Math.random.bind(Math)
	) { }

	/**
	 * 真或假
	 */
	bool() {
		return this.rand() >= 0.5;
	}

	/**
	 * 生成指定范围内的随机数 [min,max)
	 * @param min 最小值(包括)
	 * @param max 最大值(不包括)
	 */
	range(min: number, max: number) {
		return min + this.rand() * (max - min);
	}

	/**
	 * 固定步长生成指定范围内的随机数 [min,max)
	 * @param min 最小值(包括)
	 * @param max 最大值(不包括)
	 * @param step 固定步长
	 */
	rangeStep(min: number, max: number, step: number = 1) {
		const rand = min + Math.round((this.rand() * (max - min)) / step) * step;
		return rand > max ? rand - step : rand;
	}

	/**
	 * 生成指定正负范围内的随机数 [-num,num) 默认 [-1,1)
	 * @param min 最小值(包括)
	 * @param max 最大值(不包括)
	 */
	shake(num: number = 1) {
		return this.range(-num, num);
	}

	/**
	 * 生成 1 或者 -1
	 */
	sign() {
		return this.bool() ? 1 : -1;
	}

	/**
	 * 生成随机十进制颜色 0 ~ 16777215 (256 * 256 * 256 - 1)
	 */
	color(): number {
		return (this.rand() * MAX_COLOR) >> 0;
	}

	/**
	 * 权重随机
	 * @arr 权重数组 [T,weight][]
	 */
	weight<T = any>(arr: readonly [T, number][]) {
		let total = 0;
		arr.forEach(w => (total += w[1]));
		const res = this.rand() * total;
		let cur = 0;
		return arr.find(w => {
			cur += w[1];
			return cur >= res;
		})[0];
	}

	/**
	 * 从数组中随机挑选一个元素
	 * @param arr
	 */
	choose<T>(arr: readonly T[]) {
		return arr[(this.rand() * arr.length) >> 0];
	}

	/**
	 * 从数组中随机挑选一个元素并从数组中移除
	 * @param arr
	 */
	pick<T>(arr: T[]) {
		const idx = (this.rand() * arr.length) >> 0;
		return arr.splice(idx)[0];
	}

	/**
	 * 检查概率是否触发
	 * @param rate 0~1
	 */
	chance(rate: number) {
		return rate > this.rand();
	}

	/**
	 * 随机一个弧度出来
	 */
	randRad() {
		return this.rand() * MathUtil.PI2;
	}

	/**
	 * 随机一个角度出来
	 */
	randDeg() {
		return this.rand() * 360;
	}

	/**
	 * 打乱数组顺序
	 * @param arr
	 */
	randArr(arr: any[]) {
		for (let i = arr.length; --i >= 0;) {
			const randIdx = Math.floor(this.rand() * (i + 1));
			[arr[i], arr[randIdx]] = [arr[randIdx], arr[i]];
		}
		return arr;
	}
}

/**
 * 随机数工具类
 */
export const RandUtil = new Rand();
